
import { Channel } from 'amqplib'
import * as AsyncLock from 'async-lock'

export function GetChannel(url: string, topic: string, shardChannel: boolean, createChannel: (url: string, topic: string, cacheKey: string) => Promise<Channel>): Promise<Channel> {
    return ChannelCacheManager.GetChannel(url, topic, shardChannel, createChannel)
}

export function ClearChannel(): void {
    ChannelCacheManager.ClearChannel()
}

class ChannelCacheManager {
    private static _cacheChannel = new Map<string, Channel>()
    private static _lock = new AsyncLock()

    public static ClearChannel(): void {
        ChannelCacheManager._cacheChannel.forEach((channel, cacheKey, map) => {
            channel.close()
        })
        ChannelCacheManager._cacheChannel.clear();
    }

    public static GetChannel(url: string, topic: string, shardChannel: boolean, createChannel: (url: string, topic: string, cacheKey: string) => Promise<Channel>): Promise<Channel> {
        var key = topic + ' ' + url
        if (!shardChannel)
            key = Date.now() + ' ' + key

        return this.GetChannelInternal(url, topic, key, createChannel).then(channel => {
            return channel
        })
    }

    private static GetChannelInternal(url: string, topic: string, key: string, createChannel: (url: string, topic: string, cacheKey: string) => Promise<Channel>): Promise<Channel> {

        return new Promise<Channel>((resolve, reject) => {

            if (this._cacheChannel.has(key))
                return resolve(this._cacheChannel.get(key))

            var _getChannelPromise = this._lock.acquire(key, () => {
                if (ChannelCacheManager._cacheChannel.has(key))
                    return ChannelCacheManager._cacheChannel.get(key)
                else {
                    return createChannel(url, topic, key).then(channel => {
                        ChannelCacheManager._cacheChannel.set(key, channel)
                        return channel
                    })
                }
            })
            resolve(_getChannelPromise)
        })
    }
}

